/*
 * Copyright (c) 2012 Johan Fylling
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 * THE SOFTWARE.
 */

part of engine;


typedef void WorldStateListener (int id);


class WorldStateListeners {
  final List<WorldStateListener> worldObjectAdded   = new List<WorldStateListener>();
  final List<WorldStateListener> worldObjectRemoved = new List<WorldStateListener>();
  final List<WorldStateListener> actorAdded         = new List<WorldStateListener>();
  final List<WorldStateListener> actorRemoved       = new List<WorldStateListener>();
}


class WorldState {
  static final Logging.Logger _logger      = new Logging.Logger('drones.engine.world_state');
  num                         _clock       = 0;
  final Map<int, WorldObject> _objects     = new Map<int, WorldObject>();
  final SmartSet<Actor>       _actors      = new SmartSet<Actor>();
  final SmartSet<Effect>      _effects     = new SmartSet<Effect>();
  final WorldStateListeners   _listeners   = new WorldStateListeners();
  
  WorldState ();
  
  WorldStateListeners get on => this._listeners;
  
  void updateClock (num delta) {
    this._clock += delta;
  }
  
  num get clock => this._clock;
  
  bool addWorldObject (WorldObject obj) {
    if (this._objects.containsKey(obj.id)) {
      _logger.fine("WorldObject [${obj.id}] already member of World State; aborting.");
      return false;
    }
    
    this._objects[obj.id] = obj;
    _logger.fine("WorldState - World Object added: $obj");
    for (WorldStateListener listener in this._listeners.worldObjectAdded) {
      listener(obj.id);
    }
    return true;
  }
  
  WorldObject getObject (int id) {
    return this._objects[id];
  }
  
  Iterable<WorldObject> get objects {
    return this._objects.values;
  }
  
  void addActor (Actor actor) {
    this._actors.add(actor);
    _logger.fine("WorldState - Actor added: $actor");
    for (WorldStateListener listener in this._listeners.actorAdded) {
      listener(actor.id);
    }
  }
  
  void removeActor (Actor actor) {
    this._actors.remove(actor);
    _logger.fine("WorldState - Actor removed: $actor");
    for (WorldStateListener listener in this._listeners.actorRemoved) {
      listener(actor.id);
    }
  }
  
  Actor getActor (int id) {
    Actor actor = null;
    WorldObject wo = getObject(id);
    if (wo != null && wo is Actor) {
      actor = wo as Actor;
      if (this._actors.contains(actor)) {
        return actor;
      }
    }
    return actor;
  }
  
  Collection<Actor> get actors {
    return this._actors;
  }
  
  void addEffect (Effect effect) {
    this._effects.add(effect);
  }
  
  void removeEffect (Effect effect) {
    this._effects.remove(effect);
  }
  
  Collection<Effect> get effects {
    return this._effects;
  }
  
  toJson () {
    List<WorldObject> objects = new List<WorldObject>();
    for (WorldObject wo in _objects.values) {
      if (wo is! Actor || !_actors.contains(wo)) {
        objects.add(wo);
      }
    }
    
    var data = {
      "type": "world_state",
      "clock": _clock,
      "objects": objects,
      "actors": _actors.toList()
    };
    return data;
  }
}


class WorldStateStorageHandler {
  static final Logging.Logger _logger             = new Logging.Logger('drones.engine.world_state.storage');
  WorldState                  worldState          = null;
  final BroadphaseDetector    _broadphaseDetector;
  
  WorldStateStorageHandler (WorldState this.worldState, BroadphaseDetector this._broadphaseDetector);
  
  call (StorageManager manager, Map attributes) {
    if (worldState == null) {
      _logger.finer("Creating WorldState.");
      worldState = new WorldState();
    }
    
    // Set Clock
    var clock = attributes["clock"];
    assert(clock is num);
    worldState._clock = clock;
    _logger.finer("Clock set to $clock.");
    
    // Create World Objects.
    _logger.finer("Processing WorldObjects.");
    var objects = attributes["objects"];
    if (objects != null && objects is List) {
      for (Map objAttributes in objects) {
        var wo = manager.load(objAttributes);
        assert(wo is WorldObject);
        worldState.addWorldObject(wo);
        if (wo.body != null) {
          _broadphaseDetector.addDynamicBody(wo.body);
        }
      }
    }
    
    // Create Actors.
    _logger.finer("Processing Actors.");
    var actors = attributes["actors"];
    if (actors != null && actors is List) {
      for (Map actorAttributes in actors) {
        var actor = manager.load(actorAttributes);
        assert(actor is Actor);
        worldState.addWorldObject(actor);
        worldState.addActor(actor);
        if (actor.body != null) {
          _broadphaseDetector.addDynamicBody(actor.body);
        }
      }
    }
    
    return worldState;
  }
}
